/*
 * @(#)JdbcMeteDataProvider.java 1.1 2003/10/18
 * 
 * Copyright 2003 Thomas Barnekow. All Rights Reserved.
 * 
 * This software is the proprietary information of Thomas Barnekow. Use is
 * subject to license terms.
 */

package tb.provider.jdbc;

import java.sql.*;

import tb.provider.*;
import tb.util.*;

/**
 * A JDBC {@link MetaDataProvider}.
 * <p>
 * <b><i>Example:</i></b> Creating and initializing a <code>JdbcMetaDataProvider</code>
 * that creates {@link jessx.provider.Template}instances:
 * <ul>
 * <code>
 * MetaDataProvider mdp = new JdbcMetaDataProvider( <ul>
 *  new QualifiedName("MetaDataProvider.scott"), <br>
 * "jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger", <br>
 *  new JdbcTemplateBuilder()); </ul>
 * <br>
 * // Provide meta-data for schema "scott" <br>
 * mdp.provide(MetaDataProvider.DEFAULT_DESCRIPTION, new QualifiedName("scott")); <br>
 * </code>
 * </ul>
 * <p>
 * <b><i>Example:</i></b> Using that <code>JdbcMetaDataProvider</code>
 * instance in Java:
 * <ul>
 * <code>
 * MetaDataProvider mdp = (MetaDataProvider) ProviderRegistry.findProvider( <ul>
 *  MetaDataProvider.class, <br>
 *  MetaDataProvider.DEFAULT_DESCRIPTION, <br>
 *  new QualifiedName("scott")); </ul>
 * <br>
 * // Describe table "scott.emp". <br>
 * // This should return a Template instance. <br>
 * MetaObject mo = mdp.describe(new QualifiedName("scott.emp"));
 * </code>
 * </ul>
 * 
 * <b><i>Example:</i></b> Using that <code>JdbcMetaDataProvider</code>
 * instance in Jess scripts:
 * <ul>
 * <code>
 * ;; Describe entity "scott". <br>
 * ;; If the entity corresponds to a deftemplate (e.g., a table), then ?meta-object <br>
 * ;; will be bound to an instance of Template. <br>
 * ;; If the entity corresponds to a collection of templates (e.g., a schema), <br>
 * ;; then ?meta-object will be bound to a CompoundMetaObject instance <br>
 * ;; containing Template instances. <br>
 * <br>
 * (bind ?meta-object (fsm-describe scott)) <br>
 * <br>
 * <br>
 * ;; Define backchain-reactive deftemplates. <br>
 * <br>
 * (fsm-define-backchain-reactive ?meta-object)
 * </code>
 * </ul>
 * 
 * @author Thomas Barnekow
 * @version 1.0
 */
public class JdbcMetaDataProvider
	extends AbstractJdbcProvider
	implements MetaDataProvider 
{

	private Database m_database;

	private boolean m_useCatalog = false;
	private boolean m_useSchema = false;

	private Director m_director;

	/**
	 * Create an instance in shared mode.
	 * 
	 * @deprecated
	 * 
	 * @param name
	 *            this provider's qualified name
	 * @param Connection
	 *            the JDBC connection
	 * @param builder
	 *            the builder used by the {@link #describe}method in order to
	 *            create {@link MetaObject}s
	 */
	public JdbcMetaDataProvider(
		QualifiedName name,
		Connection connection,
		Builder builder) 
	{
		this(name, connection, false, false, builder);
	}

	/**
	 * Create an instance in unshared auto-connect mode.
	 * 
	 * @param name
	 *            this provider's qualified name
	 * @param url
	 *            the JDBC database URL, e.g.,
	 *            "jdbc:oracle:thin:@localhost:1521:orcl"
	 * @param user
	 *            the JDBC user, e.g., "scott"
	 * @param password
	 *            the JDBC password, e.g., "tiger"
	 * @param useCatalog
	 *            If true, tables are contained in a catalog.
	 *            Either useCatalog or useSchema should be true
	 * @param useSchema
	 *            If true, tables are contained in a schema.
	 *            Either useCatalog or useSchema should be true
	 * @param builder
	 *            the builder used by the {@link #describe}method in order to
	 *            create {@link MetaObject}s
	 */
	public JdbcMetaDataProvider(
		QualifiedName name,
		Connection connection,
		boolean useCatalog,
		boolean useSchema,
		Builder builder) 
	{
		super(name, connection);
		
		m_useCatalog = useCatalog;
		m_useSchema = useSchema;
		
		m_director = new Director(builder);
	}

	/**
	 * Create an instance in unshared auto-connect mode.
	 * 
	 * @deprecated
	 * 
	 * @param name
	 *            this provider's qualified name
	 * @param url
	 *            the JDBC database URL, e.g.,
	 *            "jdbc:oracle:thin:@localhost:1521:orcl"
	 * @param user
	 *            the JDBC user, e.g., "scott"
	 * @param password
	 *            the JDBC password, e.g., "tiger"
	 * @param builder
	 *            the builder used by the {@link #describe}method in order to
	 *            create {@link MetaObject}s
	 */
	public JdbcMetaDataProvider(
		QualifiedName name,
		String url,
		String user,
		String password,
		Builder builder) 
	{
		this(name, url, user, password, false, false, builder);
	}

	/**
	 * Create an instance in unshared auto-connect mode.
	 * 
	 * @param name
	 *            this provider's qualified name
	 * @param url
	 *            the JDBC database URL, e.g.,
	 *            "jdbc:oracle:thin:@localhost:1521:orcl"
	 * @param user
	 *            the JDBC user, e.g., "scott"
	 * @param password
	 *            the JDBC password, e.g., "tiger"
	 * @param useCatalog
	 *            If true, tables are contained in a catalog.
	 *            Either useCatalog or useSchema should be true
	 * @param useSchema
	 *            If true, tables are contained in a schema.
	 *            Either useCatalog or useSchema should be true
	 * @param builder
	 *            the builder used by the {@link #describe}method in order to
	 *            create {@link MetaObject}s
	 */
	public JdbcMetaDataProvider(
		QualifiedName name,
		String url,
		String user,
		String password,
		boolean useCatalog,
		boolean useSchema,
		Builder builder) 
	{
		super(name, url, user, password, false);

		m_useCatalog = useCatalog;
		m_useSchema = useSchema;
		
		m_director = new Director(builder);
	}

	/**
	 * Produce a {@link MetaObject}describing a {@link Schema}or a
	 * {@link Table}.
	 * 
	 * @param entity
	 *            the qualified name of a {@link Schema}(e.g., "scott") or a
	 *            {@link Table}(e.g., "scott.emp").
	 */
	public MetaObject describe(QualifiedName entity) throws Exception {
		// Find schema or table
		MetaObject metaData = getDatabase().findEntity(entity);

		// Process entity meta-data
		MetaObject result = null;
		if (metaData instanceof Schema)
			result = m_director.processSchema((Schema) metaData);
		else if (metaData instanceof Table)
			result = m_director.processTable((Table) metaData);
		else
			throw new IllegalArgumentException(
				"Unsupported entity type: " + entity);

		// Done
		releaseDatabase();
		return result;
	}

	protected Database getDatabase() throws SQLException {
		if (m_database == null) {
			m_database = new Database(getConnection(), m_useCatalog, m_useSchema);
		}
		return m_database;
	}

	protected void releaseDatabase() {
		if (isAutoDisconnectMode()) {
			m_database = null;
			closeConnection();
		}
	}

	/**
	 * This operation is not supported.
	 * 
	 * @throws UnsupportedOperationException
	 *             always!
	 */
	public void declare(QualifiedName entity, MetaObject declaration)
		throws Exception {
		throw new UnsupportedOperationException();
	}
}